Coverage Report

Created: 2021-08-28 18:14

D:\git\skunkworks\herald-for-cpp\herald\include\herald\datatype\memory_arena.h
Line
Count
Source (jump to first uncovered line)
1
//  Copyright 2021 Herald Project Contributors
2
//  SPDX-License-Identifier: Apache-2.0
3
//
4
5
#ifndef HERALD_MEMORY_ARENA_H
6
#define HERALD_MEMORY_ARENA_H
7
8
#include <cstddef>
9
#include <bitset>
10
#include <array>
11
12
/// \brief Acts as a non-global memory arena for arbitrary classes
13
namespace herald {
14
namespace datatype {
15
16
/// \brief Represents an external 'pointer' to an allocated memory area.
17
///
18
/// Used by calling classes only.
19
/// Max memory allocation in bytes is 65536
20
struct MemoryArenaEntry {
21
  unsigned short startPageIndex = 0;
22
  unsigned short byteLength = 0;
23
24
20.2k
  bool isInitialised() const {
25
20.2k
    return 0 != byteLength;
26
20.2k
  }
27
};
28
29
constexpr unsigned long pagesRequired(std::size_t size,std::size_t pageSize)
30
1.97k
{
31
1.97k
  return (size + pageSize - 1) / pageSize;
32
1.97k
}
33
34
/// \brief Very basic paged memory arena class
35
///
36
/// Can be used one arena per dynamic allocation class, or used by multiple classes.
37
/// In this non-global implementation, pass it as a static reference variable to the class
38
/// once during application startup after allocation in a main class or similar.
39
template <std::size_t MaxSize, std::size_t AllocationSize>
40
class MemoryArena {
41
public:
42
  /// \brief The Maximum size to use for data (doesn't include page table)
43
  static constexpr std::size_t Size = MaxSize;
44
  /// \brief The allocation size for each page. Note total mem usage is Size + (Size / PageSize)
45
  /// Thus for MemoryArena<2048,10>() you use 2048 + (2048 / 10) = 2253 bytes
46
  static constexpr std::size_t PageSize = AllocationSize;
47
48
  MemoryArena() noexcept
49
   : arena(), pagesInUse(false)
50
5
  {
51
5
    ;
52
5
  }
??0?$MemoryArena@$0IAA@$09@datatype@herald@@QEAA@XZ
Line
Count
Source
50
4
  {
51
4
    ;
52
4
  }
??0?$MemoryArena@$0CAAA@$07@datatype@herald@@QEAA@XZ
Line
Count
Source
50
1
  {
51
1
    ;
52
1
  }
53
54
  ~MemoryArena() = default;
55
56
265
  void reserve(MemoryArenaEntry& entry,std::size_t newSize) {
57
265
    if (newSize <= entry.byteLength) {
58
5
      return;
59
5
    }
60
260
    auto newEntry = allocate(newSize);
61
1.73k
    for (std::size_t i = 0;i < entry.byteLength;
++i1.47k
) {
62
1.47k
      set(newEntry,i,get(entry,i));
63
1.47k
    }
64
260
    deallocate(entry);
65
260
    entry = newEntry;
66
260
  }
?reserve@?$MemoryArena@$0IAA@$09@datatype@herald@@QEAAXAEAUMemoryArenaEntry@23@_K@Z
Line
Count
Source
56
3
  void reserve(MemoryArenaEntry& entry,std::size_t newSize) {
57
3
    if (newSize <= entry.byteLength) {
58
2
      return;
59
2
    }
60
1
    auto newEntry = allocate(newSize);
61
11
    for (std::size_t i = 0;i < entry.byteLength;
++i10
) {
62
10
      set(newEntry,i,get(entry,i));
63
10
    }
64
1
    deallocate(entry);
65
1
    entry = newEntry;
66
1
  }
?reserve@?$MemoryArena@$0CAAA@$07@datatype@herald@@QEAAXAEAUMemoryArenaEntry@23@_K@Z
Line
Count
Source
56
262
  void reserve(MemoryArenaEntry& entry,std::size_t newSize) {
57
262
    if (newSize <= entry.byteLength) {
58
3
      return;
59
3
    }
60
259
    auto newEntry = allocate(newSize);
61
1.72k
    for (std::size_t i = 0;i < entry.byteLength;
++i1.46k
) {
62
1.46k
      set(newEntry,i,get(entry,i));
63
1.46k
    }
64
259
    deallocate(entry);
65
259
    entry = newEntry;
66
259
  }
67
68
1.06k
  MemoryArenaEntry allocate(std::size_t size) {
69
1.06k
    if (0 == size) {
70
70
      return MemoryArenaEntry{0,0};
71
70
    }
72
999
    // find first page location with enough space
73
999
    unsigned long pages = pagesRequired(size,PageSize);
74
999
    bool inEmpty = false;
75
999
    unsigned long lastEmptyIndex = 0;
76
39.6k
    for (std::size_t i = 0;i < pagesInUse.size();
++i38.6k
) {
77
39.6k
      if (!pagesInUse.test(i)) {
78
1.80k
        // this one is empty
79
1.80k
        if (!inEmpty) {
80
1.13k
          inEmpty = true;
81
1.13k
          lastEmptyIndex = i;
82
1.13k
        }
83
1.80k
        if (i - lastEmptyIndex + 1 == pages) {
84
998
          // flip bits and return
85
2.65k
          for (unsigned long f = lastEmptyIndex;f <= i;
++f1.65k
) {
86
1.65k
            pagesInUse.set(f,true);
87
1.65k
          }
88
998
          return MemoryArenaEntry{(unsigned short)lastEmptyIndex,(unsigned short)size};
89
998
        }
90
37.8k
      } else {
91
37.8k
        inEmpty = false;
92
37.8k
      }
93
39.6k
    }
94
999
    // ran out of memory! Throw! (Causes catastrophic crash)
95
999
    
throw std::runtime_error("Unable to allocate memory in arena")1
;
96
999
  }
?allocate@?$MemoryArena@$0IAA@$09@datatype@herald@@QEAA?AUMemoryArenaEntry@23@_K@Z
Line
Count
Source
68
9
  MemoryArenaEntry allocate(std::size_t size) {
69
9
    if (0 == size) {
70
0
      return MemoryArenaEntry{0,0};
71
0
    }
72
9
    // find first page location with enough space
73
9
    unsigned long pages = pagesRequired(size,PageSize);
74
9
    bool inEmpty = false;
75
9
    unsigned long lastEmptyIndex = 0;
76
640
    for (std::size_t i = 0;i < pagesInUse.size();
++i631
) {
77
639
      if (!pagesInUse.test(i)) {
78
221
        // this one is empty
79
221
        if (!inEmpty) {
80
9
          inEmpty = true;
81
9
          lastEmptyIndex = i;
82
9
        }
83
221
        if (i - lastEmptyIndex + 1 == pages) {
84
8
          // flip bits and return
85
228
          for (unsigned long f = lastEmptyIndex;f <= i;
++f220
) {
86
220
            pagesInUse.set(f,true);
87
220
          }
88
8
          return MemoryArenaEntry{(unsigned short)lastEmptyIndex,(unsigned short)size};
89
8
        }
90
418
      } else {
91
418
        inEmpty = false;
92
418
      }
93
639
    }
94
9
    // ran out of memory! Throw! (Causes catastrophic crash)
95
9
    
throw std::runtime_error("Unable to allocate memory in arena")1
;
96
9
  }
?allocate@?$MemoryArena@$0CAAA@$07@datatype@herald@@QEAA?AUMemoryArenaEntry@23@_K@Z
Line
Count
Source
68
1.06k
  MemoryArenaEntry allocate(std::size_t size) {
69
1.06k
    if (0 == size) {
70
70
      return MemoryArenaEntry{0,0};
71
70
    }
72
990
    // find first page location with enough space
73
990
    unsigned long pages = pagesRequired(size,PageSize);
74
990
    bool inEmpty = false;
75
990
    unsigned long lastEmptyIndex = 0;
76
38.9k
    for (std::size_t i = 0;i < pagesInUse.size();
++i38.0k
) {
77
38.9k
      if (!pagesInUse.test(i)) {
78
1.58k
        // this one is empty
79
1.58k
        if (!inEmpty) {
80
1.12k
          inEmpty = true;
81
1.12k
          lastEmptyIndex = i;
82
1.12k
        }
83
1.58k
        if (i - lastEmptyIndex + 1 == pages) {
84
990
          // flip bits and return
85
2.42k
          for (unsigned long f = lastEmptyIndex;f <= i;
++f1.43k
) {
86
1.43k
            pagesInUse.set(f,true);
87
1.43k
          }
88
990
          return MemoryArenaEntry{(unsigned short)lastEmptyIndex,(unsigned short)size};
89
990
        }
90
37.4k
      } else {
91
37.4k
        inEmpty = false;
92
37.4k
      }
93
38.9k
    }
94
990
    // ran out of memory! Throw! (Causes catastrophic crash)
95
990
    
throw std::runtime_error("Unable to allocate memory in arena")0
;
96
990
  }
97
98
1.37k
  void deallocate(MemoryArenaEntry& entry) {
99
1.37k
    if (!entry.isInitialised()) {
100
422
      return; // guard
101
422
    }
102
954
    // set relevant bits to empty
103
954
    long pages = pagesRequired(entry.byteLength,PageSize);
104
2.55k
    for (int i = 0;i < pages;
++i1.60k
) {
105
1.60k
      pagesInUse.set(entry.startPageIndex + i,false);
106
1.60k
    }
107
954
    entry.byteLength = 0;
108
954
    entry.startPageIndex = 0;
109
954
  }
?deallocate@?$MemoryArena@$0IAA@$09@datatype@herald@@QEAAXAEAUMemoryArenaEntry@23@@Z
Line
Count
Source
98
4
  void deallocate(MemoryArenaEntry& entry) {
99
4
    if (!entry.isInitialised()) {
100
0
      return; // guard
101
0
    }
102
4
    // set relevant bits to empty
103
4
    long pages = pagesRequired(entry.byteLength,PageSize);
104
210
    for (int i = 0;i < pages;
++i206
) {
105
206
      pagesInUse.set(entry.startPageIndex + i,false);
106
206
    }
107
4
    entry.byteLength = 0;
108
4
    entry.startPageIndex = 0;
109
4
  }
?deallocate@?$MemoryArena@$0CAAA@$07@datatype@herald@@QEAAXAEAUMemoryArenaEntry@23@@Z
Line
Count
Source
98
1.37k
  void deallocate(MemoryArenaEntry& entry) {
99
1.37k
    if (!entry.isInitialised()) {
100
422
      return; // guard
101
422
    }
102
950
    // set relevant bits to empty
103
950
    long pages = pagesRequired(entry.byteLength,PageSize);
104
2.34k
    for (int i = 0;i < pages;
++i1.39k
) {
105
1.39k
      pagesInUse.set(entry.startPageIndex + i,false);
106
1.39k
    }
107
950
    entry.byteLength = 0;
108
950
    entry.startPageIndex = 0;
109
950
  }
110
111
8.91k
  void set(const MemoryArenaEntry& entry, unsigned short bytePosition, unsigned char value) {
112
8.91k
    if (!entry.isInitialised()) {
113
0
      return;
114
0
    }
115
8.91k
    arena[(entry.startPageIndex * PageSize) + bytePosition] = value;
116
8.91k
  }
?set@?$MemoryArena@$0IAA@$09@datatype@herald@@QEAAXAEBUMemoryArenaEntry@23@GE@Z
Line
Count
Source
111
102
  void set(const MemoryArenaEntry& entry, unsigned short bytePosition, unsigned char value) {
112
102
    if (!entry.isInitialised()) {
113
0
      return;
114
0
    }
115
102
    arena[(entry.startPageIndex * PageSize) + bytePosition] = value;
116
102
  }
?set@?$MemoryArena@$0CAAA@$07@datatype@herald@@QEAAXAEBUMemoryArenaEntry@23@GE@Z
Line
Count
Source
111
8.81k
  void set(const MemoryArenaEntry& entry, unsigned short bytePosition, unsigned char value) {
112
8.81k
    if (!entry.isInitialised()) {
113
0
      return;
114
0
    }
115
8.81k
    arena[(entry.startPageIndex * PageSize) + bytePosition] = value;
116
8.81k
  }
117
118
9.93k
  char get(const MemoryArenaEntry& entry, unsigned short bytePosition) {
119
9.93k
    if (!entry.isInitialised()) {
120
0
      return '\0';
121
0
    }
122
9.93k
    return arena[(entry.startPageIndex * PageSize) + bytePosition];
123
9.93k
  }
?get@?$MemoryArena@$0IAA@$09@datatype@herald@@QEAADAEBUMemoryArenaEntry@23@G@Z
Line
Count
Source
118
17
  char get(const MemoryArenaEntry& entry, unsigned short bytePosition) {
119
17
    if (!entry.isInitialised()) {
120
0
      return '\0';
121
0
    }
122
17
    return arena[(entry.startPageIndex * PageSize) + bytePosition];
123
17
  }
?get@?$MemoryArena@$0CAAA@$07@datatype@herald@@QEAADAEBUMemoryArenaEntry@23@G@Z
Line
Count
Source
118
9.92k
  char get(const MemoryArenaEntry& entry, unsigned short bytePosition) {
119
9.92k
    if (!entry.isInitialised()) {
120
0
      return '\0';
121
0
    }
122
9.92k
    return arena[(entry.startPageIndex * PageSize) + bytePosition];
123
9.92k
  }
124
125
12
  std::size_t pagesFree() const {
126
12
    return pagesRequired(Size,PageSize) - pagesInUse.count();
127
12
  }
128
129
private:
130
  std::array<unsigned char,Size> arena;
131
  std::bitset<pagesRequired(Size,PageSize)> pagesInUse;
132
};
133
134
}
135
}
136
137
#endif